<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="../js/css.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../js/popup.js"></script>
<title>Puzzle</title>
<style type="text/css">
.mainboard {
	position: relative;
	width: 820px;
	height: 402px;
	margin-left: auto;
	margin-right: auto;
}

.gameborder {
	position: relative;
	border: 5px solid #777777;
	width: 400px;
	height: 400px;
	float: left;
	background-color: #D9E2EA;
}

.game_main{
	position: relative;
	border: 0px;
	width: 400px;
	height: 400px;
}

.block {
	position: absolute;
	width: 98px;
	height: 98px;
	overflow: hidden;
	background-color: white;
	text-align: center;
	background-repeat: no-repeat;
	border-left: 1px solid white;
	border-right:1px solid black;
	border-top: 1px solid white;
	border-bottom: 1px solid black;
}

.img {
	position: realative;
	border: 5px solid #777777;
	width: 400px;
	height: 400px;
	float: right;
	text-align: left;
}
</style>
<script type="text/javascript">
	/*
	 *class move effect
	 */
	var MoveEffect = function(inteval, movPix) {
		this.interval = 2 || inteval;
		this.movPix = 3 || movPix;
	}
	
	MoveEffect.prototype = {
		startMove: function(obj, tox, toy, callback) {
			var offsetX = tox - obj.offsetLeft;
			var offsetY = toy - obj.offsetTop;
			var step = {x:0, y:0};
			if (Math.abs(offsetX) > Math.abs(offsetY)) {
				step.x = offsetX > 0 ? this.movPix : 0 - this.movPix;
				if (offsetY != 0) {
					var v = parseInt(Math.abs(offsetX) / Math.abs(offsetY), 10) * this.movPix;
					step.y = offsetY > 0 ? v : 0 - v;
				}
			} else {
				step.y = offsetY > 0 ? this.movPix : 0 - this.movPix;
				if (offsetX != 0) {
					var v = parseInt(Math.abs(offsetY) / Math.abs(offsetX), 10) * this.movPix;
					step.x = offsetX > 0 ? v : 0 - v;
				}
			}
			this.moveStep(obj, tox, toy, step, callback);
		},
		
		moveStep: function(obj, tox, toy, step, callback) {
			var posx = obj.offsetLeft;
			var posy = obj.offsetTop;
			if ((posx == tox && posy == toy)) {
				if (callback) {
					callback.call();
				}
				return;
			}
			
			var mov2x = step.x > 0 ? Math.min(tox, posx + step.x) : Math.max(tox, posx + step.x);
			var mov2y = step.y > 0 ? Math.min(toy, posy + step.y) : Math.max(toy, posy + step.y);
			obj.style.left = mov2x + "px";
			obj.style.top = mov2y + "px";
			
			var self = this;
			setTimeout(function() {
				self.moveStep(obj, tox, toy, step, callback);
			}, this.interval);
		}
	}
</script>
<script type="text/javascript">
	/*blank block*/
	var Blank = function(v) {
		this.value = v;
		this.x = 0;
		this.y = 0;
	}
	Blank.prototype = {
		put : function(x, y) {
			this.x = x;
			this.y = y;
		}
	}
	/*block*/
	var Block = function(w, h, v, container) {
		this.value = v;
		this.width = w;
		this.height = h;
		this.x = 0;
		this.y = 0;
		this.container = container;
		this.ui = null;
		this.initialize();
	}

	Block.prototype = {
		initialize : function() {
			this.ui = document.createElement("div");
			this.ui.className = "block";
			if (this.container != null) {
				this.container.ui.appendChild(this.ui);
				var self = this;
				this.ui.onclick = function() {
					self.container.onclickMove(self.x, self.y);
				}
			}
		},
		
		put : function(x, y, effect) {
			this.x = x;
			this.y = y;
			var pos = this.getPosition();
			
			if (effect) {
				var me = new MoveEffect();
				var self = this;
				me.startMove(this.ui, pos.left, pos.top, function() {
					self.container.moving = false;
				});
			} else {
				this.ui.style.left = pos.left + "px";
				this.ui.style.top = pos.top + "px";
			}
		},
		
		getPosition : function() {
			return this.container.getBlockPosition(this.x, this.y);
		},

		fillImage : function(x, y, img) {
			if (img == null) {
				this.ui.innerText = this.value;
			} else {
				this.ui.style.backgroundImage = "url(" + img + ")";
				this.ui.style.backgroundPositionX = x + "px";
				this.ui.style.backgroundPositionY = y + "px";
			}
		}
	}

	/*
	 * class puzzle
	 */
	var Puzzle = function(id, wn, hn) {
		this.ui = document.getElementById(id);
		this.nrow = hn;
		this.nline = wn;
		this.width = 0;
		this.height = 0;
		this.blockWidth = 0;
		this.blockHeight = 0;
		this.blocks = new Array();
		this.blankBlock = null;
		this.image = null;
		this.running = false;
		this.moving = false;

		/*initialize*/
		this.initialize();
	}

	Puzzle.prototype = {
		initialize : function() {
			this.width = this.ui.clientWidth;
			this.height = this.ui.clientHeight;
			this.blockWidth = this.width / this.nline;
			this.blockHeight = this.height / this.nrow;
		},

		setImage : function(url, id) {
			this.image = url;
			var imgobj = document.getElementById(id);
			if (!imgobj) {
				return;
			}

			imgobj.style.backgroundImage = 'url(' + url + ')';
			
		},
		
		start : function() {
			if (this.running) {
				return;
			}
			this.running = true;
			/*create blocks*/
			for (var i = 0; i < this.nrow; i++) {
				var row = new Array();
				for (var j = 0; j < this.nline; j++) {
					var value = j + i * this.nline;

					/*blank block*/
					if ((i == this.nrow - 1) && (j == this.nline - 1)) {
						this.blankBlock = new Blank(value);
						this.blankBlock.put(j, i);
						row[j] = this.blankBlock;
						continue;
					}

					var block = new Block(this.blockWidth, this.blockHeight, value, this);
					
					bx = 0 - this.blockWidth * j;
					by = 0 - this.blockHeight * i;
					block.fillImage(bx, by, this.image);
					
					block.put(j, i);
					row[j] = block;
				}
				this.blocks[i] = row;
			}
			/*shuffle blocks*/
			this.shuffle();
		},

		reset : function() {
			this.image = null;
			this.running = false;
			this.moving = false;
			
			if (this.blocks.length == 0) {
				return;
			}
			for (var i = 0; i < this.nrow; i++) {
				for (var j = 0; j < this.nline; j++) {
					var child = this.blocks[i][j].ui;
					if (child) {
						this.ui.removeChild(child);
					}
				}
			}
			this.blocks.length = 0;
		},
		
		getBlockPosition : function(x, y) {
			var left = x * this.blockWidth;
			var top = y * this.blockHeight;
			return {'left' : left, 'top' : top};
		},

		onclickMove : function(x, y) {
			if (this.moving) {
				return;
			}
			if ((y == this.blankBlock.y && Math.abs(x - this.blankBlock.x) == 1) || (x == this.blankBlock.x && Math.abs(y - this.blankBlock.y) == 1)) {
				this.moving = true;
				this.move(x, y, true);
				if (this.checkComplete()) {
					this.onComplete();
				}
			}
		},

		onComplete : function() {
			var _self = this;
			Popup.newDialog("Congratulation").show();
		},
		
		move : function(x, y, effect) {
			var bx = this.blankBlock.x;
			var by = this.blankBlock.y;
			
			var moveBlock = this.blocks[y][x];
			this.blocks[y][x] = this.blankBlock;
			this.blocks[by][bx] = moveBlock;

			moveBlock.put(bx, by, effect);
			this.blankBlock.put(x, y);
		},

		shuffle : function() {
			var level = 30;
			var prev = -1;
			for (var i = 0; i < level; i++) {
				var movPos = this.getRandomBlock(prev);
				prev = movPos.random;
				this.move(movPos.x, movPos.y);
			}
		},

		getRandomBlock : function(prev) {
			var blankX = this.blankBlock.x;
			var blankY = this.blankBlock.y;
			while(true) {
				var random = parseInt(Math.random() * 4);	//0-up,1-right,2-down,3-left
				if (prev != -1 && Math.abs(prev - random) == 2) {			//back to previous position
					continue;
				}
				var bx = blankX;
				var by = blankY;
				switch(random) {
				case 0 :
					by = blankY - 1;
					break;
				case 1 :
					bx = blankX + 1;
					break;
				case 2 :
					by = blankY + 1;
					break;
				case 3 :
					bx = blankX - 1;
					break;
				default :
					continue;
				}
				if (bx < 0 || by < 0 || bx > this.nline - 1 || by > this.nrow - 1) {
					continue;
				}
				return {'x' : bx, 'y' : by, 'random' : random};
			}
		},

		checkComplete : function() {
			var n = -1;
			for (var i = 0; i < this.nrow; i++) {
				for (var j = 0; j < this.nline; j++) {
					n += 1;
					if (n != this.blocks[i][j].value) {
						return false;
					}
				}
			}
			return true;
		}
	}

	var IMAGE_FILES = ['img/01.jpg', 'img/02.jpg', 'img/03.jpg', 'img/04.jpg'];
	var imageIndex = 0;
	var puzzle;
	window.onload = function() {
		puzzle = new Puzzle('puzzle', 4, 4);
		puzzle.setImage(IMAGE_FILES[0], 'imgdiv');
		imageIndex = 0;
		puzzle.start();
	}

	function newgame() {
		imageIndex++;
		if (imageIndex > IMAGE_FILES.length - 1) {
			imageIndex = 0;
		}
		puzzle.reset();
		puzzle.setImage(IMAGE_FILES[imageIndex], 'imgdiv');
		puzzle.start();
	}
	
	function debug(msg) {
		var consol = document.getElementById('console');
		if (!consol) {
			return;
		}
		var html = consol.innerText;
		if (html == null || html.length == 0) {
			consol.innerText = msg;
		} else {
			consol.innerText = html + "\n" + msg;
		}
	}
</script>
</head>
<body>
<div align="center">
 <div class="mainboard">
  <div class='gameborder'>
  	<div id='puzzle' class='game_main'></div>
  </div>
  <div id="imgdiv" class='img'></div>
 </div>
 <br>
 <input type='button' value='next' onclick='newgame()'>
</div>
</body>
</html>